Skip to content

ipc: add support for userspace IPC handling#10994

Open
kv2019i wants to merge 12 commits into
thesofproject:mainfrom
kv2019i:202607-user-ipc-forwarding
Open

ipc: add support for userspace IPC handling#10994
kv2019i wants to merge 12 commits into
thesofproject:mainfrom
kv2019i:202607-user-ipc-forwarding

Conversation

@kv2019i

@kv2019i kv2019i commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

This PR contains one of the remaining big subsets from #10558 and implements ability to handle a subset of IPC in a Zephyr user-space thread. This is an important enabler for running the SOF audio pipelines in user-space and allows us to run all audio module code in user context (including the module initialization and configuration functions).

This PR has no impact to default SOF builds where LL audio pipelines are run in kernel space.

kv2019i added 12 commits July 14, 2026 12:52
…e LL

When CONFIG_SOF_USERSPACE_LL is enabled the IPC context must live in
user-accessible application memory rather than being allocated from the
kernel heap and reached via sof->ipc. Place struct ipc in a dedicated
K_APP_BMEM partition and provide an out-of-line ipc_get() returning it;
drop the sof->ipc pointer in this configuration.

Add the generic ll_alloc allocation context to struct ipc and set it up
in ipc_init() from the LL user heap, so LL components (host, dai,
chain_dma) can allocate their buffers from a user-accessible heap. IPC
objects (the context itself and comp_data) are allocated accordingly.
A no-op ipc_user_init() stub is added here and implemented later.

No IPC-major-specific knowledge is added here.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Add the struct ipc_user descriptor (and its ipc_user_pdata slot in struct
ipc) that the IPC4 kernel handler uses to forward commands to the
user-space IPC thread: the two IPC4 message words, the reply fields, and
a user-readable copy of the resolved comp_driver + tr_ctx used by
user-space component creation.

In ipc4 helper, allow component buffers to be allocated from the LL user
heap (ipc->ll_alloc) and create them on the component's configured core
rather than the current CPU, so buffer creation works from the user
thread when CONFIG_SOF_USERSPACE_LL is enabled.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
…stic

Extract the per-message processing logic for module CONFIG_GET/SET and
LARGE_CONFIG_GET/SET into standalone functions that return their results
via output parameters instead of writing the global msg_reply directly,
and give SET_PIPELINE_STATE external linkage.

This is a pure refactor with no functional change. The new
ipc4_process_module_config(), ipc4_process_large_config_get(),
ipc4_process_large_config_set() and ipc4_set_pipeline_state() can be
called from any execution context, which is needed to later dispatch
these messages from a separate IPC user-space thread.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
The IPC reply and compound-message completion entry points need to be
callable from the IPC user-space thread. Declare ipc_msg_reply(),
ipc_compound_pre_start(), ipc_compound_post_start() and
ipc_wait_for_compound_msg() as Zephyr syscalls when
CONFIG_SOF_USERSPACE_LL is enabled, renaming the implementations to
z_impl_* and adding z_vrfy_* verification wrappers. A new ipc_reply.h
carries the ipc_msg_reply declaration so it can be pulled in as a
syscall header.

Register the new syscall headers in CMakeLists.txt. No functional change
for non-userspace builds, where the functions keep direct external
linkage via z_impl_* defines.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
The pipeline, component and chain-DMA bookkeeping objects must be
reachable from the user-space IPC thread, so allocate them from the
system user heap via sof_heap_alloc()/sof_heap_free() instead of the
generic rzalloc()/rfree(). As sof_heap_alloc() does not zero the
allocation, add an explicit memset() to preserve the previous behaviour.

Give ipc4_add_comp_dev() external linkage (declared in topology.h) so the
user thread can register a created component, and skip the buffer tr_ctx
copy for user-space LL builds where the kernel tr_ctx is not mapped.

No functional change for non-userspace builds.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
The user-space IPC thread reads the IPC4 module init parameter block
directly from MAILBOX_HOSTBOX_BASE (comp_new_ipc4() / comp_new_ipc4_user()),
so the HOSTBOX region must be reachable from the user LL memory domain.

Map two partitions when CONFIG_SOF_USERSPACE_LL && CONFIG_IPC_MAJOR_4:
an uncached read-only view for the parameter reads, and a cached RW view
because the sys_cache_data_invd_range() syscall verifier requires write
access to the invalidated range.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Add function user_ll_grant_access() to allow other threads
to access the scheduler mutex. This is needed if work is submitted
from other threads to the scheduler.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Add a dedicated user-space thread for handling IPC commands that operate
on audio pipelines when CONFIG_SOF_USERSPACE_LL is enabled. This provides
only the protocol-agnostic infrastructure:

 - ipc_user_init() creates the K_USER thread, allocates its k_sem/k_event
   handshake objects, sets up the memory domain and access grants, and
   spins up the LL scheduler context.
 - ipc_user_forward_cmd() forwards the two raw IPC message words from the
   kernel IPC handler to the user thread via k_event signaling, sets
   IPC_TASK_IN_THREAD so the host is not signaled until the user thread
   completes, and collects the result through a k_sem handshake.
 - ipc_user_thread_fn() runs the wait loop and delegates the actual
   command interpretation to ipc_user_thread_dispatch(), a __weak hook
   overridden by the active IPC major.

No IPC-major-specific knowledge lives here; the IPC4 dispatch is added in
a following commit. The generic infra can be extended to cover other IPC
protocol variants.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Add comp_new_ipc4_user(), called from the user-space IPC thread to create
a component from user context. It receives a pre-resolved driver pointer
from the kernel handler (which does the privileged IMR manifest / driver
list lookup), parses the IPC4 init-instance message, reads the module
init parameter block from the HOSTBOX, and calls drv->ops.create() so
untrusted module init code does not execute with kernel privileges.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Provide the IPC4 implementation of ipc_user_thread_dispatch(), the
protocol-specific hook called from the generic user-space IPC thread. It
reconstructs the IPC4 message from the forwarded primary/extension words
and runs the privilege-separated portion of each supported command:
pipeline create/delete/set-state, module config get/set, large-config
get/set, bind/unbind, and init/delete instance. Component creation goes
through comp_new_ipc4_user() so module code runs in user context.

This moves the IPC4 command switch out of the protocol-agnostic
ipc-common.c into the IPC4 handler where it belongs.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Route the IPC4 commands that operate on audio pipelines to the user-space
IPC thread via ipc_user_forward_cmd() when CONFIG_SOF_USERSPACE_LL is
enabled: create/delete/set-state pipeline, config get/set, large-config
get/set, bind/unbind and init/delete instance.

For MOD_INIT_INSTANCE the kernel still resolves the driver (needs IMR
manifest access) and copies comp_driver + tr_ctx into the user-readable
ipc_user buffer before forwarding; cross-core creation stays in kernel.
LARGE_CONFIG get/set for base firmware (module_id 0) also stay in kernel
because they touch the IMR manifest. CONFIG_GET / LARGE_CONFIG_GET copy
the reply extension (and tx payload) back from the user thread.

The now-unused ipc4_new_pipeline()/ipc4_delete_pipeline() kernel helpers
are compiled out in the user-space configuration.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
If CONFIG_SOF_BOOT_TEST_STANDALONE is set, ipc_init() is terminated
early. This ensures SOF will not start to generate or respond
to IPC messages that could potentially interfere with standalone
test cases (some of which send and receive IPCs).

The current implementation leaves the component list uninitialized
and this can cause trouble to standalone tests that want to utilzie
common IPC code to build messages.

Fix this problem by executing more of ipc_init() also in the standalone
mode.

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the Zephyr userspace LL execution model to IPC handling by forwarding selected IPC4 commands to a dedicated Zephyr user-space thread, enabling module init/config and some pipeline operations to run with reduced privileges. The changes integrate Zephyr syscall boundaries, userspace memory-domain grants (mailbox/HOSTBOX), and userspace-aware allocation paths to support privilege-separated IPC execution without affecting default kernel-space SOF builds.

Changes:

  • Add a generic “IPC user thread” forwarding mechanism (kernel IPC task → userspace IPC thread) and wire IPC4 command subsets to use it under CONFIG_SOF_USERSPACE_LL.
  • Introduce/adjust Zephyr syscall interfaces for IPC reply + IPC4 compound message helpers to support userspace callers.
  • Update IPC4 helper allocation/core bookkeeping to support userspace execution and user-heap allocations.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
zephyr/lib/userspace_helper.c Grants userspace access to HOSTBOX (cached + uncached mappings) for IPC4 module init/config payload reads.
zephyr/Kconfig Adds SOF_IPC_USER_THREAD_STACK_SIZE Kconfig for the IPC userspace thread stack.
zephyr/include/rtos/sof.h Hides sof->ipc when userspace LL is enabled (IPC context is no longer stored there).
zephyr/CMakeLists.txt Adds syscall header generation for new IPC reply and IPC4 handler syscall declarations.
src/schedule/zephyr_ll.c Adds user_ll_grant_access() to grant userspace thread access to LL scheduler locks.
src/ipc/ipc4/helper.c Adds userspace component creation entrypoint and shifts several allocations/frees to user-heap helpers.
src/ipc/ipc4/handler-user.c Forwards selected IPC4 commands to userspace and adds userspace-side dispatch/processing helpers.
src/ipc/ipc4/handler-kernel.c Converts select IPC helpers to Zephyr syscall implementations with verification wrappers.
src/ipc/ipc3/helper.c Renames IPC reply implementation to z_impl_ipc_msg_reply() for syscall compatibility.
src/ipc/ipc-helper.c Adjusts buffer trace ctx init for userspace LL and updates component container free to user-heap free helper.
src/ipc/ipc-common.c Implements IPC userspace thread infrastructure, forwarding API, and userspace-aware IPC init path.
src/include/sof/schedule/ll_schedule_domain.h Exposes user_ll_grant_access() prototype.
src/include/sof/ipc/topology.h Exposes IPC4 userspace creation helpers and ipc4_add_comp_dev() for userspace dispatch.
src/include/sof/ipc/ipc_reply.h New header providing syscall-friendly ipc_msg_reply() declaration/mapping.
src/include/sof/ipc/common.h Adds struct ipc_user, userspace IPC APIs, and wires IPC reply via the new header.
src/include/ipc4/handler.h Exposes new IPC4 helpers and declares syscall variants for compound-message helpers under userspace LL.

Comment thread src/ipc/ipc-common.c
Comment on lines +360 to +364
ret = k_sem_take(pdata->sem, K_MSEC(10));
if (ret) {
LOG_ERR("IPC user: sem error %d\n", ret);
return ret;
}
Comment thread src/ipc/ipc-common.c
Comment on lines +462 to +466
user_grant_dai_access_all(&ipc_user_thread);
user_grant_dma_access_all(&ipc_user_thread);
user_access_to_mailbox(zephyr_ll_mem_domain(), &ipc_user_thread);
user_ll_grant_access(&ipc_user_thread, PLATFORM_PRIMARY_CORE_ID);
k_mem_domain_add_thread(zephyr_ll_mem_domain(), &ipc_user_thread);
Comment thread src/ipc/ipc-common.c
Comment on lines +535 to 538
if (!ipc->comp_data) {
tr_err(&ipc_tr, "Unable to allocate IPC component data");
rfree(sof->ipc);
sof_heap_free(heap, ipc);
return -ENOMEM;
Comment thread src/ipc/ipc-common.c
Comment on lines +582 to +584
ipc_user_init();

return platform_ipc_init(ipc);
Comment on lines +1536 to +1537
memcpy_s(&mi, sizeof(mi), ipc4, sizeof(*ipc4));
if (!cpu_is_me(mi.extension.r.core_id)) {
Comment on lines +1710 to +1717
case SOF_IPC4_MOD_BIND: {
struct ipc4_module_bind_unbind bu;

memcpy_s(&bu, sizeof(bu), &msg, sizeof(msg));
result = ipc_comp_connect(ipc_user->ipc,
(ipc_pipe_comp_connect *)&bu);
break;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants